package pacman.protocol;

import java.io.IOException;
import java.util.ArrayList;

import maze.Barcode;
import maze.Coordinate;
import maze.Direction;
import maze.Location;
import maze.Maze;
import pacman.Game;
import robots.Ghost;
import robots.OwnGhost;

public class GameProtocolHandler {
	
	private boolean VERBOSE = true;
	
	private Game game;
	
	public GameProtocolHandler(Game game){
		this.game = game;
	}
	/**
	 * Processes the JOIN command
	 */
	public void processJOIN(){
		if(!game.readySetGo()){ //Gewone verloop van spel
			if(game.numberOfJoins >= Game.NUMBEROFGHOSTS){
				System.err.println("processJOIN terwijl er al 4 JOINs geregistreerd waren");
			}else{
				game.numberOfJoins++;
				if(VERBOSE) System.out.println(game.getOwnGhost().getName() + " heeft een JOIN ontvangen en heeft er nu al " + game.numberOfJoins);
				if(game.numberOfJoins >= Game.NUMBEROFGHOSTS){ 
					if(VERBOSE) System.out.println(game.getOwnGhost().getName() + " heeft 4 joins ontvangen!");
					game.stayInJoinPhase = false;
					
					if(VERBOSE) System.out.println(game.getOwnGhost().getName() + " stuurt zijn naam");
					game.getOwnGhost().protocolSender.sendNAME(); //NAME sturen 4 JOINs ontvangen
				}
			}
		}else{ //Spel was al geinitialiseerd, JOIN komt van gecrashte robot
			System.out.println(game.getOwnGhost().getName() + " ontvangt een JOIN in een game die readysetgo");
			game.getOwnGhost().protocolSender.sendRENAME();
		}
	}
	
	/**
	 * Processes the NAME command
	 */
	public void processNAME(String name, String version){
		//Eigen naam moet niet verwerkt worden
		if(name.equals(game.getOwnGhost().getName())) return;
		
		if(!game.readySetGo()){ //Normale verloop van het spel
			Ghost newGhost = new Ghost(new Coordinate(0, 0), game, name);
			newGhost.setVersion(version);
			newGhost.hasSendName = true;
			game.addGhost(newGhost);
			
			game.numberOfNamesRegistered++;
			if(VERBOSE) System.out.println(game.getOwnGhost().getName() + " heeft de naam van " + name + " ontvangen en heeft nu " + game.numberOfNamesRegistered + " geregistreerd (zichzelf inc).");
			
			game.stayInJoinPhase = false; //Na ontvangen van een naam moet niet langer gewacht worden op 4 JOINs
			if(!game.getOwnGhost().hasSendName){
				//Naam nog niet verstuurd dus nu wel doen als antwoord op eerste NAME
				game.getOwnGhost().protocolSender.sendNAME();
			}
		}else{ //NAME ontvangen na start spel = gecrashte ghost
			game.processCrashedRobot(name, version);
		}
	}
	
	public void processRENAME(String name, String version){
		//Eigen naam moet niet verwerkt worden
		if(name.equals(game.getOwnGhost().getName())) return;
		
		if(!game.readySetGo()){
			game.crashedGame = true;
			//RENAME ontvangen, je komt dus toe in een game die al gestart is
			//JOINS enzo hoeven niet, enkel je game vullen met ghosts
			int index = game.numberOfNamesRegistered;
			game.getGhosts()[index] = new Ghost(game.robotStartCoordinates[index], game,  name);
			game.getGhosts()[index].setName(name);
			game.getGhosts()[index].setVersion(version);
			game.getGhosts()[index].setSendName(true);
			game.numberOfNamesRegistered++;
			if(VERBOSE) System.out.println(game.getOwnGhost().getName() + " heeft de naam van " + name + " ontvangen (RENAME) en heeft nu " + game.numberOfNamesRegistered + " geregistreerd (zichzelf inc).");
			game.stayInJoinPhase = false; //Na ontvangen van een naam moet niet langer gewacht worden op 4 JOINs
			if(!game.getOwnGhost().hasSendName){
				//Naam nog niet verstuurd dus nu wel doen als antwoord op eerste NAME
				game.getOwnGhost().protocolSender.sendNAME();
			}
		}else{
			//Als je nog in de game zit moet je niets doen met de RENAME's
			//Enkel de gecrashte ghost gebruikt dit commando
		}
	}
	
	/**
	 * Processes the PACMAN command
	 * by adding the location to the list of all locations where pacman has been seen.
	 */
	public void processPACMAN(String name, int x, int y){
		//Juist zetten van wie de laatste keer PACMAN gestuurd heeft. Nodig voor REPACMAN
		if(game.getOwnGhostWithName(name) != null)
			game.getOwnGhostWithName(name).protocolSender.lastOneToSendPacman = true;
		for(OwnGhost otherGhost: game.getOtherOwnGhostsThan(name)){
			otherGhost.protocolSender.lastOneToSendPacman = false;
			((OwnGhostProtocolProcessor)(otherGhost.protocolProcessor)).processPACMAN(name,x, y);
		}
		game.getLastPacmanLocations().add(new Coordinate(x, y ));
	}
	/**
	 * Processes the PACMAN command by adding the location to the list of all locations where pacman has been seen.
	 */
	public void processREPACMAN(String name, int x, int y){
		processPACMAN(name, x, y);
	}
	
	/**
	 * Processes the CAPTURED command
	 */
	public void processCAPTURED(String name){
		game.getGhostWithName(name).protocolProcessor.processCAPTURED();
	}

	/**
	 * Processes the PLAN command
	 */
	public void processPLAN(String name, ArrayList<Location> plan){
		game.getGhostWithName(name).protocolProcessor.processPLAN(plan);
	}
	
	/**
	 * Processes the CANCELPLAN command.
	 */
	public void processCANCELPLAN(String name){
		game.getGhostWithName(name).protocolProcessor.processCANCELPLAN();
	}
	
	/**
	 * Processes the PING command
	 */
	public void processPING(String sender, String destination, String message){
		//Niet reageren op eigen PING
		if(sender.equals(game.getOwnGhost().getName())) return;
		
		if(destination.equals("*")){
			for(OwnGhost ghost: game.getOwnGhosts()){
				ghost.protocolProcessor.processPING(sender, message);
			}
		}else if(game.getOwnGhosts().contains(game.getGhostWithName(destination))){
			game.getGhostWithName(destination).protocolProcessor.processPING(sender, message);
		}else{
			System.err.println("Geen ownGhost gevonden met naam " + destination + " in game van " + game.getOwnGhost().getName());
		}
	}
	
	/**
	 * Processes the PONG command
	 */
	public void processPONG(String sender, String destination, String message){
		if(game.getOwnGhosts().contains(game.getGhostWithName(destination))){
			game.getGhostWithName(destination).protocolProcessor.processPONG(sender, message);
		}
	}
	
	/**
	 * Processes the POSITION command
	 */
	public void processPOSITION(String name, int x, int y){
		if(name.equalsIgnoreCase("pacman")){
			game.getPacman().setCoordinate(new Coordinate(x, y));
		}else{
			game.getGhostPositions().put(game.getGhostWithName(name), new Coordinate(x,y));
		}
	}
	
	/**
	 * Processes the REPOSITION command
	 */
	public void processREPOSITION(String name, int x, int y){
		processPOSITION(name, x, y);
	}
	
	/**
	 * Processes the DISCOVER command
	 */
	public void processDISCOVER(String senderName, int x, int y, int north, int east, int south, int west){
		//Nog geen DISCOVER verwerken als je game nog niet geinitialiseerd is
		if(!game.readySetGo()) return;
		Ghost sendingGhost = game.getGhostWithName(senderName);
		if (!senderName.equals(game.getOwnGhost().getName())){
			try{
		if(north != 2)
			sendingGhost.getMaze().write(x, y, maze.Direction.NORTH, (char) (2-north), true);
		if(east != 2)
			sendingGhost.getMaze().write(x, y, maze.Direction.EAST, (char) (2-east), true);
		if(south != 2)
			sendingGhost.getMaze().write(x, y, maze.Direction.SOUTH, (char) (2-south), true);
		if(west != 2)
			sendingGhost.getMaze().write(x, y, maze.Direction.WEST, (char) (2-west), true);
			}catch(Exception e){}
		}
		for(Ghost receivingGhost: game.getOtherGhostsThan(senderName)){
//			if(VERBOSE) receivingGhost.printMazes();
			Coordinate coord = new Coordinate(x,y);
			receivingGhost.protocolProcessor.processDISCOVER(coord, 2-north, 2-east, 2-south, 2-west, true, senderName);
			Barcode barcodeRec = getSameBarcode(receivingGhost, sendingGhost);
			if(barcodeRec != null && !receivingGhost.getName().equals(senderName)){
				Barcode barcodeSend = sendingGhost.getMaze().getBarcodeOnString(barcodeRec.getCode());
				Direction recdir = (barcodeRec.getCode().equals(barcodeSend.getCode()))?barcodeRec.getInDirection():barcodeRec.getInDirection().opposite();
				if(receivingGhost.getMazesOtherRobots()==null){
					System.err.println("mazesOtherRObots is null in game van " + game.getOwnGhost().getName());
				}
				if(receivingGhost.getMazesOtherRobots().get(senderName)==null){
					System.err.println("get(" + senderName + ") is null in game van " + game.getOwnGhost().getName());
				}
				if(receivingGhost.getMazesOtherRobots().get(senderName).match(barcodeSend.getPosition().getX(), barcodeSend.getPosition().getY(), barcodeRec.getPosition().getX(), barcodeRec.getPosition().getY(), barcodeSend.getInDirection(), recdir)){
					receivingGhost.addMatchingGhost(sendingGhost);
					sendingGhost.addMatchingGhost(receivingGhost);
				}
			}
		}
	}
	
	/**
	 * Processes the REDISCOVER command
	 */
	public void processREDISCOVER(String senderName, int x, int y, int north, int east, int south, int west){
		processDISCOVER(senderName, x, y, north, east, south, west);
	}
	
	/**
	 * controleer of er 2 robots zijn met dezelfde barcode.
	 * @return 	null als er geen dezelfde barcodes zijn
	 * 			barcode als er dezelfde barcode is.
	 */
	private Barcode getSameBarcode(Ghost receiver, Ghost sender){
		try{
			for(Barcode senderBarcode: sender.getMaze().getBarcodes()){
				for(Barcode receiverBarcode: receiver.getMaze().getBarcodes()){
					if(senderBarcode.equals(receiverBarcode)) return receiverBarcode;
				}
			}
		}catch(NullPointerException e){
			System.err.println("Nullpointer in controlBarcode");
		}
		return null;
	}
	
	/**
	 * Processes the BARCODEAT command
	 * Een binaire barcode van de vorm abcdef (waarbij elke letter een 1 of een
	 * 0 voorstelt) wordt voorgesteld door het gehele getal
	 * 2^5a+2^4b+2^3c+2^2d+2^1e+2^0f.
	 */
	public void processBARCODEAT(String senderName, Coordinate co, int barcode, int direction){
		if (senderName.equals(game.getOwnGhost().getName())) return;
		String bc = Integer.toBinaryString(barcode*2);
		while (bc.length() < 8){
			bc = 0 + bc;
		}
//		if(direction == 3 || direction == 2){ //String reversen als ze niet in de richten zijn 
//			StringBuffer reverser = new StringBuffer(bc);   //Die overeenkomt zoals nodig voor addBarcode
//			bc = new String(reverser.reverse());
//		}
		Ghost ghost = game.getGhostWithName(senderName);
		
		Maze ghostMaze = ghost.getMaze();
		if(ghostMaze == null) System.err.println("GHOSTMAZE in game van " + game.getOwnGhost().getName() + " voor ghost " + ghost.getName() + " is null");
		ghostMaze.addBarcode(co, bc, direction);
// 		if (direction == 1 || direction == 3){ //Strings zijn al gereversed dus voor verticale richtingen
//			ghostMaze.addBarcode(new Coordinate(co.getX(), co.getY()), bc, 1);
//		}else{
//			ghostMaze.addBarcode(new Coordinate(co.getX(), co.getY()), bc, 0);
//		}
	}
	/**
	 * Processes the REBARCODEAT command
	 */
	public void processREBARCODEAT(String senderName, Coordinate co, int barcode, int direction){
		processBARCODEAT(senderName, co, barcode, direction);
	}
	
	/**
	 * Processes the UNDOBARCODE command
	 * @param string
	 * @param coordinate
	 */
	public void processUNDOBARCODE(String senderName, Coordinate coordinate) {
		Ghost senderGhost = game.getGhostWithName(senderName);
		senderGhost.getMaze().removeBarcode(coordinate);
	}
	public void processSHOWMAP(String destination) throws IOException {
		if(destination.equals(game.getOwnGhost().getName())){
			game.getOwnGhost().protocolProcessor.processSHOWMAP();
		}
	}
}
